package projp2p;
import java.util.*;
import java.io.*;
import java.net.*;

public class Mestre{
	
	class ThreadRequisicao extends Thread{
		private Socket s;
		public ThreadRequisicao(Socket s){
			super();
			this.s = s;
			
		}
		
		public void run(){

			System.out.println("Conexao aceita, processando requisicao");
			String message = null;
			try {
				String nomeHost = s.getInetAddress().getCanonicalHostName();
				if(!listaPeers.contains(nomeHost))
					listaPeers.add(nomeHost);
				
				BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
				message = br.readLine();
				System.out.println("Mensagem lida");
				processaRequisicao(message);
				br.close();
				s.close();
				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			

			
		}
		
		private void responde(String resposta){
			try {
				System.out.println("Tentando escrever");
				PrintWriter pw = new PrintWriter(s.getOutputStream());
				pw.println(resposta);
				pw.flush();
				System.out.println("Mensagem enviada");
			} catch (IOException e) {
				e.printStackTrace();
			}	
		}
		
		private void processaRequisicao(String message){
			System.out.println("Processando requisicao");
			if(message == null){
				throw new NullPointerException();
			}
			System.out.println(message);
			
			String[] tokens = message.split("\\|");
			String op = tokens[0];
			if(op==null) System.out.println("op eh nulo");
			System.out.println(op);
			//Operacao LISTAR
			if(op.trim().equals("listar")){
				System.out.println("Operacao listar");
				StringBuffer sb = new StringBuffer();
				for(Arquivo each : mapaArquivosNos.keySet()){
					if(mapaArquivosNos.get(each).size() > 0)
						sb.append(each.getNome()+"|");
				}
				if(sb.length() > 0)
					sb.deleteCharAt(sb.length()-1);
				String resposta = sb.toString();
				
				try {
					System.out.println("Tentando escrever");
					PrintWriter pw = new PrintWriter(s.getOutputStream());
					pw.println(resposta);
					pw.flush();
					System.out.println("Mensagem enviada");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}else
			//Operacao PROCURAR
			if(op.equals("procurar")){
				String nomeArq = tokens[1];
				System.out.println("Operacao procurar");
				StringBuffer sb = new StringBuffer();
				String resposta;
				if(mapaArquivosNos.get(new Arquivo(nomeArq)) != null){
					for(String each : mapaArquivosNos.get(new Arquivo(nomeArq))){
						sb.append(each+"|");
					}
					resposta = sb.toString();
				}
				else
					resposta = "";
				
				try {
					System.out.println("Tentando escrever");
					PrintWriter pw = new PrintWriter(s.getOutputStream());
					pw.println(resposta);
					pw.flush();
					System.out.println("Mensagem enviada");
				} catch (IOException e) {
					e.printStackTrace();
				}		
			}else
				//REGISTRAR
			if(op.equals("registrar")){
				System.out.println("Operacao registrar");
				String nomeArq = tokens[1];
				String nomeHost = s.getInetAddress().getCanonicalHostName();
				Arquivo ar = new Arquivo(nomeArq, new Date());
				
				if(mapaArquivosNos.get(ar) != null){
					ArrayList<String> al = mapaArquivosNos.get(ar);
					if(!al.contains(nomeHost))
						al.add(nomeHost);
					
					responde("EXISTE");
				}else{
					ArrayList<String> al = new ArrayList<String>();
					al.add(nomeHost);
					
					mapaArquivosNos.put(ar, al);
					responde("OK");
				}

			}else
			if(op.equals("excluir")){
				System.out.println("Operacao excluir");
				String nomeArq = tokens[1];
				String nomeHost = s.getInetAddress().getCanonicalHostName();
				Arquivo ar = new Arquivo(nomeArq, new Date());
				if(mapaArquivosNos.get(ar) != null){
					ArrayList<String> al = mapaArquivosNos.get(ar);
					/*boolean rolou = */al.remove(nomeHost);
					if(al.size() == 0){
						mapaArquivosNos.remove(ar);
						System.out.println("Arquivo removido totalmente do compartilhamento");
					}
					System.out.println("Operacao excluir terminada");
				}else{
					System.out.println("Operacao excluir falhou");
				}
			}else
			if(op.equals("tchau")){
				System.out.println("Peer desconectou");
				String nomeHost = s.getInetAddress().getCanonicalHostName();
				listaPeers.remove(nomeHost);
				Iterator<Arquivo> it = mapaArquivosNos.keySet().iterator();
				while(it.hasNext()){
					Arquivo each = it.next();
					ArrayList<String> al = mapaArquivosNos.get(each);
					al.remove(nomeHost);
					if(al.size() == 0){
						it.remove();
						System.out.println("Arquivo removido totalmente do compartilhamento");
					}
				}
			}else
			if(op.equals("lock")){
				for(Arquivo each : mapaArquivosNos.keySet()){
					if(each.getNome().equals(tokens[1])){
						if(!each.emUso()){
							each.setEmUso(true);
							responde("OK");
							anuncio(message);
						}
					}
				}
				responde("ERRO");	
			}else
			if(op.equals("unlock")){
				for(Arquivo each : mapaArquivosNos.keySet()){
					if(each.getNome().equals(tokens[1])){
						if(each.emUso()){
							each.setEmUso(false);
							//responde("OK");
							anuncio(message);
						}
					}
				}
				responde("ERRO");
			}else
			if(op.equals("atualizar")){
				ArrayList<String> al = mapaArquivosNos.get(tokens[1]);
				if(al != null){
					al.clear();
					String nomeHost = s.getInetAddress().getCanonicalHostName();
					al.add(nomeHost);
					
					anuncio(message);
				}else{
					new NullPointerException().printStackTrace();
				}
			}
		}
		
		
	}
	
	private Hashtable<Arquivo, ArrayList<String>> mapaArquivosNos;
	private ArrayList<String> listaPeers;
	private ServerSocket serverSocket;
	private boolean terminar = false;
	
	public void anuncio(String msg){
		for(String peer: listaPeers)
			anuncioParaPeer(msg, peer);
	}
	
	public void anuncioParaPeer(String msg, String host){

		try {
			Socket s = new Socket(host, Globals.PEER_LISTEN_PORT);
			PrintWriter pw = new PrintWriter(s.getOutputStream());
			
			pw.println(msg);
			pw.flush();

			pw.close();
			s.close();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		
	}
	
	public Mestre(){
		listaPeers = new ArrayList<String>();
		mapaArquivosNos = new Hashtable<Arquivo, ArrayList<String>>();

		try{
			
			serverSocket = new ServerSocket(Globals.LISTEN_PORT);
			while(!terminar){
				Socket s = serverSocket.accept();
				ThreadRequisicao tr = new ThreadRequisicao(s);
				tr.run();
				
			}
		}catch(IOException exc){
			exc.printStackTrace();
		}
	}
	
	
	public static void main(String[] args) {
		new Mestre();

	}
}
